﻿using System;
using System.Globalization;
using System.Text;
using GeoAPI.Geometries;
using YJKGridTopologyCal.Algorithm;

namespace YJKGridTopologyCal.Geometries
{
	[Serializable]
	public class LineSegment : IComparable
	{
		public Coordinate P1
		{
			get
			{
				return this._p1;
			}
			set
			{
				this._p1 = value;
			}
		}

		public Coordinate P0
		{
			get
			{
				return this._p0;
			}
			set
			{
				this._p0 = value;
			}
		}

		public LineSegment(Coordinate p0, Coordinate p1)
		{
			this._p0 = p0;
			this._p1 = p1;
		}

		public LineSegment(LineSegment ls) : this(ls._p0, ls._p1)
		{
		}

		public LineSegment() : this(new Coordinate(), new Coordinate())
		{
		}

		public LineSegment(double x0, double y0, double x1, double y1) : this(new Coordinate(x0, y0), new Coordinate(x1, y1))
		{
		}

		public Coordinate GetCoordinate(int i)
		{
			if (i != 0)
			{
				return this._p1;
			}
			return this._p0;
		}

		public void SetCoordinates(LineSegment ls)
		{
			this.SetCoordinates(ls._p0, ls._p1);
		}

		public void SetCoordinates(Coordinate p0, Coordinate p1)
		{
			this._p0.X = p0.X;
			this._p0.Y = p0.Y;
			this._p1.X = p1.X;
			this._p1.Y = p1.Y;
		}

		public double MinX
		{
			get
			{
				return Math.Min(this.P0.X, this.P1.X);
			}
		}

		public double MaxX
		{
			get
			{
				return Math.Max(this.P0.X, this.P1.X);
			}
		}

		public double MinY
		{
			get
			{
				return Math.Min(this.P0.Y, this.P1.Y);
			}
		}

		public double MaxY
		{
			get
			{
				return Math.Max(this.P0.Y, this.P1.Y);
			}
		}

		public double Length
		{
			get
			{
				return this._p0.Distance(this._p1);
			}
		}

		public bool IsHorizontal
		{
			get
			{
				return this._p0.Y == this._p1.Y;
			}
		}

		public bool IsVertical
		{
			get
			{
				return this._p0.X == this._p1.X;
			}
		}

		public int OrientationIndex(LineSegment seg)
		{
			int num = CGAlgorithms.OrientationIndex(this._p0, this._p1, seg._p0);
			int num2 = CGAlgorithms.OrientationIndex(this._p0, this._p1, seg._p1);
			if (num >= 0 && num2 >= 0)
			{
				return Math.Max(num, num2);
			}
			if (num <= 0 && num2 <= 0)
			{
				return Math.Max(num, num2);
			}
			return 0;
		}

		public int OrientationIndex(Coordinate p)
		{
			return CGAlgorithms.OrientationIndex(this._p0, this._p1, p);
		}

		public void Reverse()
		{
			Coordinate p = this._p0;
			this._p0 = this._p1;
			this._p1 = p;
		}

		public void Normalize()
		{
			if (this._p1.CompareTo(this._p0) < 0)
			{
				this.Reverse();
			}
		}

		public double Angle
		{
			get
			{
				return Math.Atan2(this._p1.Y - this._p0.Y, this._p1.X - this._p0.X);
			}
		}

		public Coordinate MidPoint
		{
			get
			{
				return new Coordinate((this._p0.X + this._p1.X) / 2.0, (this._p0.Y + this._p1.Y) / 2.0);
			}
		}

		public double Distance(LineSegment ls)
		{
			return CGAlgorithms.DistanceLineLine(this._p0, this._p1, ls._p0, ls._p1);
		}

		public double Distance(Coordinate p)
		{
			return CGAlgorithms.DistancePointLine(p, this._p0, this._p1);
		}

		public double DistancePerpendicular(Coordinate p)
		{
			return CGAlgorithms.DistancePointLinePerpendicular(p, this._p0, this._p1);
		}

		public Coordinate PointAlong(double segmentLengthFraction)
		{
			return new Coordinate
			{
				X = this._p0.X + segmentLengthFraction * (this._p1.X - this._p0.X),
				Y = this._p0.Y + segmentLengthFraction * (this._p1.Y - this._p0.Y)
			};
		}

		public Coordinate PointAlongOffset(double segmentLengthFraction, double offsetDistance)
		{
			double num = this._p0.X + segmentLengthFraction * (this._p1.X - this._p0.X);
			double num2 = this._p0.Y + segmentLengthFraction * (this._p1.Y - this._p0.Y);
			double num3 = this._p1.X - this._p0.X;
			double num4 = this._p1.Y - this._p0.Y;
			double num5 = Math.Sqrt(num3 * num3 + num4 * num4);
			double num6 = 0.0;
			double num7 = 0.0;
			if (offsetDistance != 0.0)
			{
				if (num5 <= 0.0)
				{
					throw new ApplicationException("Cannot compute offset from zero-length line segment");
				}
				num6 = offsetDistance * num3 / num5;
				num7 = offsetDistance * num4 / num5;
			}
			double x = num - num7;
			double y = num2 + num6;
			return new Coordinate(x, y);
		}

		public double ProjectionFactor(Coordinate p)
		{
			if (p.Equals(this._p0))
			{
				return 0.0;
			}
			if (p.Equals(this._p1))
			{
				return 1.0;
			}
			double num = this._p1.X - this._p0.X;
			double num2 = this._p1.Y - this._p0.Y;
			double num3 = num * num + num2 * num2;
			if (num3 <= 0.0)
			{
				return double.NaN;
			}
			return ((p.X - this._p0.X) * num + (p.Y - this._p0.Y) * num2) / num3;
		}

		public double SegmentFraction(Coordinate inputPt)
		{
			double num = this.ProjectionFactor(inputPt);
			if (num < 0.0)
			{
				num = 0.0;
			}
			else if (num > 1.0)
			{
				num = 1.0;
			}
			return num;
		}

		public Coordinate Project(Coordinate p)
		{
			if (p.Equals(this._p0) || p.Equals(this._p1))
			{
				return new Coordinate(p);
			}
			double num = this.ProjectionFactor(p);
			return new Coordinate
			{
				X = this._p0.X + num * (this._p1.X - this._p0.X),
				Y = this._p0.Y + num * (this._p1.Y - this._p0.Y)
			};
		}

		public LineSegment Project(LineSegment seg)
		{
			double num = this.ProjectionFactor(seg._p0);
			double num2 = this.ProjectionFactor(seg._p1);
			if (num >= 1.0 && num2 >= 1.0)
			{
				return null;
			}
			if (num <= 0.0 && num2 <= 0.0)
			{
				return null;
			}
			Coordinate p = this.Project(seg._p0);
			if (num < 0.0)
			{
				p = this._p0;
			}
			if (num > 1.0)
			{
				p = this._p1;
			}
			Coordinate p2 = this.Project(seg._p1);
			if (num2 < 0.0)
			{
				p2 = this._p0;
			}
			if (num2 > 1.0)
			{
				p2 = this._p1;
			}
			return new LineSegment(p, p2);
		}

		public Coordinate ClosestPoint(Coordinate p)
		{
			double num = this.ProjectionFactor(p);
			if (num > 0.0 && num < 1.0)
			{
				return this.Project(p);
			}
			double num2 = this._p0.Distance(p);
			double num3 = this._p1.Distance(p);
			if (num2 >= num3)
			{
				return this._p1;
			}
			return this._p0;
		}

		public Coordinate[] ClosestPoints(LineSegment line)
		{
			Coordinate coordinate = this.Intersection(line);
			if (coordinate != null)
			{
				return new Coordinate[]
				{
					coordinate,
					coordinate
				};
			}
			Coordinate[] array = new Coordinate[2];
			Coordinate coordinate2 = this.ClosestPoint(line._p0);
			double num = coordinate2.Distance(line._p0);
			array[0] = coordinate2;
			array[1] = line._p0;
			Coordinate coordinate3 = this.ClosestPoint(line._p1);
			double num2 = coordinate3.Distance(line._p1);
			if (num2 < num)
			{
				num = num2;
				array[0] = coordinate3;
				array[1] = line._p1;
			}
			Coordinate coordinate4 = line.ClosestPoint(this._p0);
			num2 = coordinate4.Distance(this._p0);
			if (num2 < num)
			{
				num = num2;
				array[0] = this._p0;
				array[1] = coordinate4;
			}
			Coordinate coordinate5 = line.ClosestPoint(this._p1);
			num2 = coordinate5.Distance(this._p1);
			if (num2 < num)
			{
				array[0] = this._p1;
				array[1] = coordinate5;
			}
			return array;
		}

		public Coordinate Intersection(LineSegment line)
		{
			LineIntersector lineIntersector = new RobustLineIntersector();
			lineIntersector.ComputeIntersection(this._p0, this._p1, line._p0, line._p1);
			if (lineIntersector.HasIntersection)
			{
				return lineIntersector.GetIntersection(0);
			}
			return null;
		}

		public Coordinate LineIntersection(LineSegment line)
		{
			try
			{
				return HCoordinate.Intersection(this._p0, this._p1, line._p0, line._p1);
			}
			catch (NotRepresentableException)
			{
			}
			return null;
		}

		public ILineString ToGeometry(IGeometryFactory geomFactory)
		{
			return geomFactory.CreateLineString(new Coordinate[]
			{
				this._p0,
				this._p1
			});
		}

		public override bool Equals(object o)
		{
			if (o == null)
			{
				return false;
			}
			if (!(o is LineSegment))
			{
				return false;
			}
			LineSegment lineSegment = (LineSegment)o;
			return this._p0.Equals(lineSegment._p0) && this._p1.Equals(lineSegment._p1);
		}

		public static bool operator ==(LineSegment obj1, LineSegment obj2)
		{
			return object.Equals(obj1, obj2);
		}

		public static bool operator !=(LineSegment obj1, LineSegment obj2)
		{
			return !(obj1 == obj2);
		}

		public int CompareTo(object o)
		{
			LineSegment lineSegment = (LineSegment)o;
			int num = this._p0.CompareTo(lineSegment._p0);
			if (num == 0)
			{
				return this._p1.CompareTo(lineSegment._p1);
			}
			return num;
		}

		public bool EqualsTopologically(LineSegment other)
		{
			return (this._p0.Equals(other._p0) && this._p1.Equals(other._p1)) || (this._p0.Equals(other._p1) && this._p1.Equals(other._p0));
		}

		public override string ToString()
		{
			StringBuilder stringBuilder = new StringBuilder("LINESTRING( ");
			stringBuilder.AppendFormat(LineSegment._cultureInfo, "{0}", new object[]
			{
				this._p0.X
			}).Append(" ");
			stringBuilder.AppendFormat(LineSegment._cultureInfo, "{0}", new object[]
			{
				this._p0.Y
			}).Append(", ");
			stringBuilder.AppendFormat(LineSegment._cultureInfo, "{0}", new object[]
			{
				this._p1.X
			}).Append(" ");
			stringBuilder.AppendFormat(LineSegment._cultureInfo, "{0}", new object[]
			{
				this._p1.Y
			}).Append(")");
			return stringBuilder.ToString();
		}

		public override int GetHashCode()
		{
			long num = BitConverter.DoubleToInt64Bits(this._p0.X);
			num ^= BitConverter.DoubleToInt64Bits(this._p0.Y) * 31L;
			int num2 = (int)num ^ (int)(num >> 32);
			long num3 = BitConverter.DoubleToInt64Bits(this._p1.X);
			num3 ^= BitConverter.DoubleToInt64Bits(this._p1.Y) * 31L;
			int num4 = (int)num3 ^ (int)(num3 >> 32);
			return num2 ^ num4;
		}

		private Coordinate _p0;

		private Coordinate _p1;

		private static readonly CultureInfo _cultureInfo = CultureInfo.InvariantCulture;
	}
}
